<?php
require_once 'Hamlap/Base.php';
require_once 'Hamlap/Html/Element.php';
require_once 'Hamlap/Html/Element/Text.php';
/**
 * Hamlap_Html handles the HTML output.
 *
 * @link http://haml-lang.com/ Original Haml parser (for Ruby)
 * @license http://www.opensource.org/licenses/mit-license.php MIT (X11) License
 * @author Rik Bruil <info@rikbruil.com>
 * @package Hamlap
 */
class Hamlap_Html extends Hamlap_Base
{
	/**
	 * The root elements
	 * @var array
	 */
	private $root = null;

	/**
	 * Array containing the doctypes
	 * @var unknown_type
	 */
	private $doctypes = array
	(
		'html' 			=> '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
		'html_frames'	=> '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
		'html_strict'	=> '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
		'xhtml'			=> '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
		'xhtml_strict'	=> '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
		'xhtml_basic'	=> '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">',
		'xhtml_1.1'		=> '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
	);

	/**
	 * Contains all the tagnames for closed elements
	 * @var array
	 */
	static private $_closed = array('br', 'hr', 'link', 'meta', 'img', 'input');

	/**
	 * Contains all the tagnames for inline elements
	 * @var array
	 */
	static private $_inline = array
	(
		'a', 'strong', 'b', 'em', 'i', 'span', 'dt',
		'dd', 'cite', 'td', 'th', 'abbr', 'acronym',
		'title', 'img'
	);


	/**
	 *
	 * @param array $options
	 */
	public function __construct(array $options = array())
	{
		// set the options
		$this->setOptions($options);

		// set debug mode
		// TODO Implement debug mode
		if($this->getOption('debug') === null)
			$this->setOption('debug', false);

		// tell elements whether to use escaping or not
		if($this->getOption('escape'))
			Hamlap_Html_Element::escapeAttributes($this->getOption('escape'));
	}

	/**
	 * Set the root elements for this document
	 * @param array $elements
	 * @return Hamlap_Html
	 */
	public function setRoot(array $elements)
	{
		$this->root = $elements;
		return $this;
	}

	/**
	 * Get the root element for this document
	 * @return array
	 */
	public function getRoot()
	{
		return $this->root;
	}

	/**
	 * Create a new HTML element with given options
	 * @param array $options
	 * @return Hamlap_Html_Element_Text|Hamlap_Html_Element
	 */
	public function createElement(array $options = array())
	{
		// create the element
		if(!$this->elementIsText($options))
		{
			$element = new Hamlap_Html_Element();

			// specify the attributes
			foreach($options as $k => $v)
				$element->setAttribute($k, $v);

			// check if the element is text-only
			$element->setTextOnly($element->isTextOnly($options));
		}
		else
			$element = new Hamlap_Html_Element_Text($options['text']);

		return $element;
	}

	/**
	 * Check if a element only consists of text
	 * @param array $options
	 * @return boolean
	 */
	private function elementIsText(array $options = array())
	{
		foreach($options as $k => $v)
			if($k != 'text' && $k != 'indent' && !empty($v))
				return false;

		return true;
	}

	/**
	 * Render the document
	 * @return string
	 */
	public function render()
	{
		$document = '';

		// check if a doctype was set
		if($this->getOption('doctype'))
		{
			// check if elements need to be rendered as XHTML
			if(strstr($this->getOption('doctype'), 'xhtml') !== false)
				Hamlap_Html_Element::useXhtml();

			// setup doctype
			$document.= $this->doctypes[$this->getOption('doctype')]."\n".$document;
		}

		// render the actual elements
		if(count($this->root))
			foreach($this->root[0] as $k => $element)
				$document.= $element->render();

		// reset the root array
		$this->root = array();

		return trim($document);
	}

	public function __set($var, $val)
	{
		$var = strtolower($var);

		switch($var)
		{
			case 'root':
				$this->setRoot($val);
				break;
			case 'options':
				$this->setOptions($val);
				break;
			default:
				$this->setOption($var, $val);
				break;
		}
	}

	public function __get($var)
	{
		$var = strtolower(trim($var));

		switch($var)
		{
			case 'root':
				return $this->getRoot();
				break;
			case 'options':
				return $this->getOptions();
				break;
			default:
				return $this->getOption($var);
				break;
		}
	}

	public function __isset($var)
	{
		$var = strtolower(trim($var));

		switch($var)
		{
			case 'root':
				return isset($this->root);
				break;
			case 'options':
				return isset($this->options);
				break;
			default:
				return isset($this->options[$var]);
				break;
		}
	}

	/**
	 * Set an option by the specified name and value
	 * @param string $name
	 * @param mixed $value
	 * @return Hamlap_Haml
	 */
	public function setOption($name, $value)
	{
		return parent::setOption($name, $value);
	}

	/**
	 * Set the options passed as key-value paired array
	 * @param array $options
	 * @return Hamlap_Haml
	 */
	public function setOptions(array $options = array())
	{
		return parent::setOptions($options);
	}

	/**
	 * Check if the given tagname is an inline element
	 * @param string $tag
	 * @return boolean
	 */
	static public function isInline($tag)
	{
		return in_array($tag, self::$_inline);
	}

	/**
	 * Check if the given tagname is a closed element
	 * @param string $tag
	 * @return boolean
	 */
	static public function isClosed($tag)
	{
		return in_array($tag, self::$_closed);
	}
}